package com.tianji.learning.service.impl;

import cn.hutool.core.util.ObjectUtil;
import cn.hutool.db.DbRuntimeException;
import com.tianji.api.client.course.CourseClient;
import com.tianji.api.dto.course.CourseFullInfoDTO;
import com.tianji.api.dto.leanring.LearningLessonDTO;
import com.tianji.api.dto.leanring.LearningRecordDTO;
import com.tianji.common.utils.AssertUtils;
import com.tianji.common.utils.BeanUtils;
import com.tianji.common.utils.ObjectUtils;
import com.tianji.common.utils.UserContext;
import com.tianji.learning.domain.dto.LearningRecordFormDTO;
import com.tianji.learning.domain.po.LearningLesson;
import com.tianji.learning.domain.po.LearningRecord;
import com.tianji.learning.enums.LessonStatus;
import com.tianji.learning.enums.SectionType;
import com.tianji.learning.utils.LearningRecordHandler;
import com.tianji.learning.mapper.LearningRecordMapper;
import com.tianji.learning.service.ILearningLessonService;
import com.tianji.learning.service.ILearningRecordService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;

/**
 * <p>
 * 学习记录表 服务实现类
 * </p>
 *
 * @author LYQ
 * @since 2024-06-22
 */
@Service
@RequiredArgsConstructor
public class LearningRecordServiceImpl extends ServiceImpl<LearningRecordMapper, LearningRecord> implements ILearningRecordService {

    private final ILearningLessonService learningLessonService;
    private final CourseClient courseClient;
    private final LearningRecordHandler learningRecordHandler;


    /**
     * 1、查询当前用户对应课程学习进度
     *
     * @param courseId
     * @return
     */
    @Override
    public LearningLessonDTO queryLearningRecordByCourse(Long courseId) {
        //初始LearningLessonDTO
        LearningLessonDTO learningLessonDTO = new LearningLessonDTO();
        //1、根据userId 和 courseId 查询课表
        LearningLesson learningLesson = learningLessonService.queryLessonByUserIdAndCourseId(UserContext.getUser(), courseId);
        //2、查询学习记录
        List<LearningRecord> list = this.lambdaQuery().eq(LearningRecord::getLessonId, learningLesson.getId()).list();
        //3、填充数据
        learningLessonDTO.setId(learningLesson.getId());
        learningLessonDTO.setLatestSectionId(learningLesson.getLatestSectionId());
        learningLessonDTO.setRecords(BeanUtils.copyToList(list, LearningRecordDTO.class));
        return learningLessonDTO;
    }

    /**
     * 2、添加学习记录
     *
     * @param learningRecordFormDTO
     */
    @Override
    @Transactional
    public void addLearningRecord(LearningRecordFormDTO learningRecordFormDTO) {
        //定义该次提交的学习记录是否完成
        boolean firstFinished;
        //1、处理学习提交记录
        if (learningRecordFormDTO.getSectionType() == SectionType.EXAM) {
            //1.1处理考试学习记录
            firstFinished = handlerExamRecord(learningRecordFormDTO);
        } else {
            //1.2处理视频学习记录
            firstFinished = handlerVideoRecord(learningRecordFormDTO);
        }
        //如果是暂存至redis则无需进行learingLseeon表修改
        if (!firstFinished && learningRecordFormDTO.getDuration() == -1){
            return;
        }
        //2、处理课表数据
        handlerLearingLesson(firstFinished, learningRecordFormDTO);
    }

    private boolean handlerVideoRecord(LearningRecordFormDTO learningRecordFormDTO) {
        //1、从redis中获取旧的学习记录，如果redis中没有就从数据库中获取并存入redis中
        LearningRecord oldLearningRecord = getOldLearningRecord(learningRecordFormDTO);
        //2、判断学习记录是否存在
        if (ObjectUtils.isEmpty(oldLearningRecord)) {
            //2.1如果不存在,则为新增的学习记录，转为po填充数据后存入数据库
            LearningRecord newLearningRecord = BeanUtils.toBean(learningRecordFormDTO, LearningRecord.class);
            newLearningRecord.setUserId(UserContext.getUser());
            newLearningRecord.setFinishTime(learningRecordFormDTO.getCommitTime());
            boolean saveResult = this.save(newLearningRecord);
            if (!saveResult) {
                throw new DbRuntimeException("保存学习记录失败");
            }
            //2.1.1、返回该新增学习记录结果为false
            return false;
        } else {
            //2.2如果存在,则判断该学习是否已经完成
            boolean firstFinished = (!oldLearningRecord.getFinished() && oldLearningRecord.getMoment() * 2 >= learningRecordFormDTO.getDuration());
            if (!firstFinished) {
                //2.2.1如果没有学完，将该学习记录存入redis中（合并写请求优化）
                //1）填充需要存入redis中newRecord的属性
                LearningRecord newRecord = new LearningRecord();
                newRecord.setLessonId(oldLearningRecord.getLessonId());
                newRecord.setFinished(oldLearningRecord.getFinished());
                newRecord.setSectionId(oldLearningRecord.getSectionId());
                newRecord.setMoment(learningRecordFormDTO.getMoment());
                newRecord.setId(oldLearningRecord.getId());
                //2）存入redis的同时提交延迟检测任务
                learningRecordHandler.LearningRecordDelayCheckTask(newRecord);
                //3）返回该新增学习记录结果为false
                learningRecordFormDTO.setDuration(-1);
                return false;
            }
            //2.2.1如果第一次已学完
            //1）更新学习记录
            boolean updateResult = this.lambdaUpdate()
                    .set(LearningRecord::getMoment, learningRecordFormDTO.getMoment())
                    .set(firstFinished, LearningRecord::getFinished, true)
                    .set(firstFinished, LearningRecord::getCreateTime, learningRecordFormDTO.getCommitTime())
                    .eq(LearningRecord::getId, oldLearningRecord.getId())
                    .update();
            if (!updateResult) {
                throw new DbRuntimeException("更新学习记录失败");
            }
            //2）清除缓存
            learningRecordHandler.deleteLeraningRecordFromCache(oldLearningRecord);
            //3）、返回是否完成该学习的boolean值
            return firstFinished;
        }
    }

    private LearningRecord getOldLearningRecord(LearningRecordFormDTO learningRecordFormDTO) {
        //1、从redis中查询learningRecord
        LearningRecord learningRecord = learningRecordHandler.getLeanringRecordFromCache(learningRecordFormDTO);

        //2、如果redis中存在直接返回
        if (ObjectUtil.isNotEmpty(learningRecord)) {
          return learningRecord;
        }
        //2.1、无则从mysql中查询
        learningRecord = this.lambdaQuery()
                .eq(LearningRecord::getLessonId, learningRecordFormDTO.getLessonId())
                .eq(LearningRecord::getSectionId, learningRecordFormDTO.getSectionId())
                .one();
        //3、如果mysql中也未查到，则返回null
        if (ObjectUtil.isEmpty(learningRecord)) {
            return null;
        }
        //3.1若查到则将其存入redis中
        learningRecordHandler.addLearningRecordToCache(learningRecord);
        //4、最终将learningrecord返回
        return learningRecord;
    }

    private boolean handlerExamRecord(LearningRecordFormDTO learningRecordFormDTO) {
        //1、转为po
        LearningRecord learningRecord = BeanUtils.toBean(learningRecordFormDTO, LearningRecord.class);
        //2、填充数据
        learningRecord.setFinished(true);
        learningRecord.setUserId(UserContext.getUser());
        learningRecord.setFinishTime(learningRecordFormDTO.getCommitTime());
        //3、返回结果为true（表示已完成该学习）
        return true;
    }

    /**
     * 新增学习记录时，对课表信息的处理
     *
     * @param firstFinished
     * @param learningRecordFormDTO
     */
    private void handlerLearingLesson(boolean firstFinished, LearningRecordFormDTO learningRecordFormDTO) {
        //1、查询对应课表数据
        LearningLesson learningLesson = learningLessonService.getById(learningRecordFormDTO.getLessonId());
        AssertUtils.isNotNull(learningLesson, "课表数据不存在，无法更新课表数据");
        //2、存入必定更新的字段
        //2.1存入基本字段更新数据
        learningLesson.setLatestSectionId(learningRecordFormDTO.getSectionId());
        learningLesson.setLatestLearnTime(learningRecordFormDTO.getCommitTime());
        if (learningLesson.getStatus() == LessonStatus.NOT_BEGIN) {
            //2.2判断之前是否开始学习课程，如果第一次开始课程的学习，则更新课表状态为学习中
            learningLesson.setStatus(LessonStatus.LEARNING);
        }
        //3、判断是否完成该新增学习记录的课程小节学习
        if (firstFinished) {
            //3.1查询对应的课程数据用来比较是否已经完成该课程的全部学习
            CourseFullInfoDTO courseInfo = courseClient.getCourseInfoById(learningLesson.getCourseId(), false, false);
            AssertUtils.isNotNull(courseInfo, "课表数据不存在，无法更新课表数据");
            if (courseInfo.getSectionNum() <= learningLesson.getLearnedSections() + 1) {
                //3.2.2如果完成该课程的全部学习，则更新课表状态
                learningLesson.setStatus(LessonStatus.FINISHED);
            }
            //3.3已学习章节数+1
            learningLesson.setLearnedSections(learningLesson.getLearnedSections() + 1);
        }
        //4、所有需要更新字段已经重新存入对象，更新数据库课表数据
//        boolean updateResult = learningLessonService.updateById(learningLesson);
        boolean updateResult = learningLessonService.lambdaUpdate()
                .set(LearningLesson::getLatestSectionId, learningLesson.getLatestSectionId())
                .set(LearningLesson::getLatestLearnTime, learningLesson.getLatestLearnTime())
                .set(LearningLesson::getStatus, learningLesson.getStatus())
                .set(LearningLesson::getLearnedSections, learningLesson.getLearnedSections())
                .eq(LearningLesson::getId, learningLesson.getId())
                .update();
        AssertUtils.isTrue(updateResult, "更新课表数据失败");
    }

}
